﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DynamicFiltering
{
    
    /// <summary>
    /// This class is used when EntityModelNavigator finds multiple chains of the same length between the 2 entities passed to 
    /// BuildShortestChain.
    /// Note that in the majority of cases, this won't be used.  If you have entities a mile apart, this may not work.
    /// In that case, we use the 2 criteria below to attempt to find the better chain among the alternatives:
    /// - Most references in entities in the chain to the target entity by name.
    ///     Ex: If my target entity is "Child" find the chain that has the most intermediate entities that have "Child" in their name.
    ///         If we've named things appropriately, you should find a more logically direct path this way.
    /// - Least ZeroOrOne count entities in the chain.
    /// </summary>
    public class EntityChainQuality
    {

        public LinkedList<NavigationItem> Chain { get; set; }
        public int ZeroOrOneCount { get; set; }
        public int ToEntityNameCount { get; set; }

        public static LinkedList<NavigationItem> HighestQualityChain( List<EntityChainQuality> ecqList )
        {
            var mostEntityNames = EcqObjectsWithMostEntityNames( ecqList );
            if ( mostEntityNames.Count == 1 )
            {
                return mostEntityNames[0].Chain;
            }
            else
            {
                var leastZeroReferences = FindListItemsWithLowestCountOf<EntityChainQuality>( ecqList, ( eca ) => eca.ZeroOrOneCount );
                if ( leastZeroReferences.Count == 1 )
                {
                    return leastZeroReferences[0].Chain;
                }
                else
                {
                    /// Pick first from list with most target entity names.
                    /// This list will have more than 1 item in it (will always have at least one and we've already
                    /// checked for that above).
                    return mostEntityNames[0].Chain;
                }
            }
        }

        /// <summary>
        /// Used because this logic is repeated
        /// </summary>
        /// <returns>A sublist of items who have the lowest count of whatever is returned from compareProperty</returns>
        public static List<T> FindListItemsWithLowestCountOf<T>( List<T> list, Func<T, int> compareProperty )
        {
            var least = int.MaxValue;
            list.ForEach( li =>
            {
                var currentListCheckVal = compareProperty( li );
                if ( currentListCheckVal < least )
                {
                    least = currentListCheckVal;
                }
            } );

            var result = new List<T>();
            list.ForEach( li =>
            {
                var currentListCheckVal = compareProperty( li );
                if ( currentListCheckVal == least )
                {
                    result.Add( li );
                }
            } );
            return result;
        }

        /// <summary>
        /// Just like above method except we're finding the most, rather than the least
        /// </summary>
        private static List<EntityChainQuality> EcqObjectsWithMostEntityNames( List<EntityChainQuality> ecqList )
        {
            var mostNames = 0;
            var result = new List<EntityChainQuality>();
            ecqList.ForEach( ecq =>
            {
                if ( ecq.ToEntityNameCount >= mostNames )
                {
                    result.Add( ecq );
                    mostNames = ecq.ToEntityNameCount;
                }
            } );
            return result;
        }

    }

}
